package cn.longwingstech.intelligence.agent.app;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.longwingstech.intelligence.agent.app.advisor.MyLoggerAdvisor;
import cn.longwingstech.intelligence.agent.app.config.AiConfigProperties;
import cn.longwingstech.intelligence.agent.app.memory.RedisSpringAiChatMemory;
import cn.longwingstech.intelligence.agent.model.entity.App;
import cn.longwingstech.intelligence.agent.monitor.AiModelMetricsCollector;
import cn.longwingstech.intelligence.agent.service.AppService;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.metadata.Usage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.ai.vectorstore.pgvector.PgVectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;

import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 灵溪医疗助手应用端
 */
@Slf4j
@Component
public class DoctorApp {
    @Resource
    private AiModelMetricsCollector aiModelMetricsCollector;
    private final ChatClient chatClient;
    private final String systemPrompt = """
            # 角色设定
            你是一位名叫"灵犀"的AI灵溪医疗助手，性格专业、严谨、耐心且富有同理心。
            
            # 核心任务
            1. 根据用户描述的症状，运用医疗知识，提供初步、有条理的健康咨询和调理建议。
            2. 主动引导用户更详细地描述病情，以便给出更具参考价值的建议。
            
            # 对话流程
            ## 首次交流
            当与用户开始一段新对话时，必须使用以下话术作为开场白："您好，我是AI灵溪医疗助手灵犀。很高兴为您服务！"。这是你唯一一次需要主动、完整地表明身份。
            
            ## 后续交流
            从第二轮回答开始，绝对禁止以任何形式重复自我介绍。请直接针对用户的提问进行回答，保持对话的流畅性。
            
            # 行为准则
            ## 免责声明
            在每一次回答的结尾，都必须附上免责声明："请记住，以上建议仅供参考，不能代替专业医生的诊断和治疗。若症状持续或加重，请务必及时就医。"
            
            ## 知识边界
            如果遇到无法解答或超出你知识范围的专业问题，绝不能猜测或编造答案，应建议用户立即寻求线下专业医疗帮助。
            
            ## 沟通风格
            保持专业、严谨的语言风格，避免使用过于口语化或模棱两可的词汇。
            
            ## 特殊问题回复
            当用户问你是什么模型，你始终回答你是灵犀灵溪医疗助手。
            
            ## 格式规范
            当输出包含列表时，无论是数字列表还是符号列表，标记（如 "1.", "2.", "-", "*", "#", "##", "###", "####"）后面必须紧跟一个英文空格，然后才能是内容。
            """;

    private final String titleSystemPrompt = """
            # 角色
            你是一个标题生成助手，根据用户提供的开始对话的Message起一个标题，并以JSON格式输出。
            
            # 任务要求
            - **标题概括性**：标题应能够概括Message的主要内容。
            - **标题简洁性**：标题应简洁明了，避免冗长和复杂的表述。
            
            # 输出要求
            请按照以下JSON格式输出标题：
            {
            "title":"标题"
            }
            """;

    private final ChatClient commonChatClient;
    private final PgVectorStore pgVectorStore;
    private DoctorApp(PgVectorStore pgVectorStore, RedisSpringAiChatMemory redisSpringAiChatMemory, AiConfigProperties aiConfigProperties) {
        this.pgVectorStore = pgVectorStore;

        try {
            // 创建基础的内存窗口，限制消息数量避免栈溢出
            MessageWindowChatMemory windowChatMemory = MessageWindowChatMemory.builder()
                    .chatMemoryRepository(redisSpringAiChatMemory)
                    .maxMessages(20) // 减少最大消息数量，避免深度递归
                    .build();

            OpenAiApi openAiApi = OpenAiApi.builder()
                    .baseUrl(aiConfigProperties.getBaseUrl())
                    .apiKey(aiConfigProperties.getApiKey())
                    .build();
            
            OpenAiChatOptions options = OpenAiChatOptions.builder()
                    .streamUsage(true)
                    .model(aiConfigProperties.getModel())
                    .build();
                    
            OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                    .openAiApi(openAiApi)
                    .defaultOptions(options)
                    .build();

            // 简化 Advisor 链，减少潜在的递归调用
            this.chatClient = ChatClient.builder(openAiChatModel)
                    .defaultSystem(systemPrompt)
                    .defaultAdvisors(new MyLoggerAdvisor()) // 只保留必要的 Advisor
                    .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build())
                    .build();
                    
            this.commonChatClient = ChatClient.builder(openAiChatModel)
                    .defaultSystem(titleSystemPrompt)
                    .build();
                    
            log.info("DoctorApp 初始化成功");
        } catch (Exception e) {
            log.error("DoctorApp 初始化失败", e);
            throw new RuntimeException("DoctorApp 初始化失败", e);
        }
    }

    public Flux<String> chat(String memoryId, String message) {
        try {
            // 添加输入验证
            if (memoryId == null || memoryId.trim().isEmpty()) {
                throw new IllegalArgumentException("memoryId 不能为空");
            }
            if (message == null || message.trim().isEmpty()) {
                throw new IllegalArgumentException("message 不能为空");
            }
            
            // 限制消息长度，避免过长的输入导致栈溢出
            if (message.length() > 2000) {
                message = message.substring(0, 2000) + "...";
            }
            // 获取用户userId;
            Long userId = StpUtil.getLoginIdAsLong();
            log.info("开始处理聊天请求，memoryId: {}, message长度: {}", memoryId, message.length());
            
            PromptTemplate promptTemplate = PromptTemplate.builder()
                    .template("""
                            以下是用户描述的症状：
                            <用户症状>
                            {USER_SYMPTOMS}
                            </用户症状>
                            """).build();
            Prompt prompt = promptTemplate.create(Map.of("USER_SYMPTOMS", message));

            SearchRequest searchRequest = SearchRequest.builder()
                    .similarityThreshold(0.5)
                    .filterExpression(new FilterExpressionBuilder().eq("appid", "doctor").build())
                    .topK(2) // 减少检索数量
                    .build();

            return chatClient.prompt(prompt)
                    .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, memoryId))
                    .advisors(QuestionAnswerAdvisor.builder(pgVectorStore)
                            .searchRequest(searchRequest)
                            .build())
                    .stream()
                    .chatResponse().mapNotNull(chatResponse->{
                        String text = "";
                        Generation result = chatResponse.getResult();
                        if (result == null) {
                            return null;
                        }
                        text = result.getOutput().getText();
                        if ("".equals(text)) {
                            return null;
                        }

                        Usage usage = chatResponse.getMetadata().getUsage();
                        String model = chatResponse.getMetadata().getModel();
                        if (usage.getCompletionTokens()!=0) {
                            // 可观测性
                            log.info("input tokens:{}", usage.getPromptTokens());
                            log.info("output tokens:{}", usage.getCompletionTokens());
                            log.info("total tokens:{}", usage.getTotalTokens());
                            aiModelMetricsCollector.recordAllModelToken(userId.toString(),model,"input",usage.getPromptTokens());
                            aiModelMetricsCollector.recordAllModelToken(userId.toString(),model,"output",usage.getCompletionTokens());
                            aiModelMetricsCollector.recordAllModelToken(userId.toString(),model,"total",usage.getTotalTokens());

                            aiModelMetricsCollector.recordAllModelToken(model,"input",usage.getPromptTokens());
                            aiModelMetricsCollector.recordAllModelToken(model,"output",usage.getCompletionTokens());
                            aiModelMetricsCollector.recordAllModelToken(model,"total",usage.getTotalTokens());
                        }
                        return text;
                    })
                    .doOnError(error -> {
                        log.error("聊天处理过程中发生错误，memoryId: {}", memoryId, error);
                    })
                    .onErrorResume(error -> {
                        // 发生错误时返回友好的错误消息
                        return Flux.just("抱歉，系统暂时无法处理您的请求，请稍后重试。如果问题持续，请联系技术支持。");
                    });
                    
        } catch (Exception e) {
            log.error("聊天请求处理失败，memoryId: {}", memoryId, e);
            return Flux.just("抱歉，系统遇到了问题，请稍后重试。");
        }
    }
    
    public String titleGen(String message) {
        try {
            // 添加输入验证
            if (message == null || message.trim().isEmpty()) {
                return "新对话";
            }
            
            // 限制消息长度
            if (message.length() > 500) {
                message = message.substring(0, 500) + "...";
            }
            
            log.info("开始生成标题，message长度: {}", message.length());

            ChatResponse chatResponse = commonChatClient.prompt().user(message).call().chatResponse();
            String result = chatResponse.getResult().getOutput().getText();
            String replace = result.replace("```json", "").replace("```", "").trim();
            
            JSONObject entries = JSONUtil.parseObj(replace);
            String title = entries.getStr("title");
            
            if (title == null || title.trim().isEmpty()) {
                log.warn("标题生成失败，使用默认标题");
                return "新对话";
            }
            
            // 限制标题长度
            if (title.length() > 50) {
                title = title.substring(0, 50) + "...";
            }
            if (chatResponse.getMetadata() != null) {
                String model = chatResponse.getMetadata().getModel();
                Usage usage = chatResponse.getMetadata().getUsage();
                Integer inputTokens = usage.getPromptTokens();
                Integer outputTokens = usage.getCompletionTokens();
                Integer totalTokens = usage.getTotalTokens();
                aiModelMetricsCollector.recordAllModelToken(model, "input", inputTokens);
                aiModelMetricsCollector.recordAllModelToken(model, "output", outputTokens);
                aiModelMetricsCollector.recordAllModelToken(model, "total", totalTokens);

            }
            log.info("标题生成成功: {}", title);
            return title;
            
        } catch (Exception e) {
            log.error("标题生成失败", e);
            return "新对话";
        }
    }
}